package com.Unidbg_hengdin.ndk;

import com.alibaba.fastjson.util.IOUtils;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Module;
import com.github.unidbg.arm.backend.Backend;
import com.github.unidbg.arm.backend.CodeHook;
import com.github.unidbg.arm.backend.UnHook;
import com.github.unidbg.arm.backend.Unicorn2Factory;
import com.github.unidbg.arm.context.Arm64RegisterContext;
import com.github.unidbg.debugger.Debugger;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.*;
import com.github.unidbg.linux.android.dvm.array.ByteArray;
import com.github.unidbg.memory.Memory;
import com.github.unidbg.pointer.UnidbgPointer;
import com.github.unidbg.utils.Inspector;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;

public class NativeHelper1 {
    private final AndroidEmulator emulator;
    private final VM vm;
    private final Module module;

    private final DvmClass NativeHelperUtils;

    private final boolean logging;

    NativeHelper1(boolean logging) {
        this.logging = logging;

        emulator = AndroidEmulatorBuilder.for64Bit()
                .setProcessName("com.xiaojianbang.app")
                .addBackendFactory(new Unicorn2Factory(true))
                .build(); // 创建模拟器实例，要模拟32位或者64位，在这里区分
        final Memory memory = emulator.getMemory(); // 模拟器的内存操作接口
        memory.setLibraryResolver(new AndroidResolver(23)); // 设置系统类库解析


        vm = emulator.createDalvikVM(); // 创建Android虚拟机
        vm.setVerbose(logging); // 设置是否打印Jni调用细节      unidbg-android/src/test/java/com/Unidbg_hengdin/ndk/libxiaojianbang.so
        vm.setJni(new AbstractJni() {
            @Override
            public DvmObject<?> callObjectMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) {
                System.out.println("signature ： " + signature);
                if(signature.equals("java/lang/String->getBytes(Ljava/lang/String;)[B")){
                    String args = (String)dvmObject.getValue();

                    System.out.println("args :" + args);
                    byte[] byteArray = args.getBytes();
                    return new ByteArray(vm,byteArray);
                }
                return super.callObjectMethodV(vm, dvmObject, signature, vaList);
            }
        });

//        DalvikModule dmA = vm.loadLibrary(new File("unidbg-android/src/test/java/com/Unidbg_hengdin/ndk/libxiaojianbangA.so"), false); // 加载libttEncrypt.so到unicorn虚拟内存，加载成功以后会默认调用init_array等函数
        DalvikModule dm = vm.loadLibrary(new File("unidbg-android/src/test/java/com/Unidbg_hengdin/ndk/libxiaojianbang.so"), false); // 加载libttEncrypt.so到unicorn虚拟内存，加载成功以后会默认调用init_array等函数
//        dm.callJNI_OnLoad(emulator); // 手动执行JNI_OnLoad函数
        module = dm.getModule(); // 加载好的libttEncrypt.so对应为一个模块

        NativeHelperUtils = vm.resolveClass("com/xiaojianbang/ndk/NativeHelper");
    }

    void destroy() {
        IOUtils.close(emulator);
        if (logging) {
            System.out.println("destroy");
        }
    }

    public static void main(String[] args) throws Exception {
        NativeHelper1 test = new NativeHelper1(true);

//        int data = test.callFun();
        test.callFun();

//        Inspector.inspect(data, "callFun");
//
        test.destroy();
    }

    void callFun() {
//        if (logging) {
//            Symbol sbox0 = module.findSymbolByName("sbox0"); // 在libttEncrypt.so模块中查找sbox0导出符号
//            Symbol sbox1 = module.findSymbolByName("sbox1");
//            Inspector.inspect(sbox0.createPointer(emulator).getByteArray(0, 256), "sbox0"); // 打印sbox0导出符号在unicorn中的内存数据
//            Inspector.inspect(sbox1.createPointer(emulator).getByteArray(0, 256), "sbox1");
//
//            IHookZz hookZz = HookZz.getInstance(emulator); // 加载HookZz，支持inline hook，文档看https://github.com/jmpews/HookZz
//            hookZz.enable_arm_arm64_b_branch(); // 测试enable_arm_arm64_b_branch，可有可无
//            hookZz.wrap(module.findSymbolByName("ss_encrypt"), new WrapCallback<RegisterContext>() { // inline wrap导出函数
//                @Override
//                public void preCall(Emulator<?> emulator, RegisterContext ctx, HookEntryInfo info) {
//                    Pointer pointer = ctx.getPointerArg(2);
//                    int length = ctx.getIntArg(3);
//                    byte[] key = pointer.getByteArray(0, length);
//                    Inspector.inspect(key, "ss_encrypt key");
//                }
//                @Override
//                public void postCall(Emulator<?> emulator, RegisterContext ctx, HookEntryInfo info) {
//                    System.out.println("ss_encrypt.postCall R0=" + ctx.getLongArg(0));
//                }
//            });
//            hookZz.disable_arm_arm64_b_branch();
//            hookZz.instrument(module.base + 0x00000F5C + 1, new InstrumentCallback<Arm32RegisterContext>() {
//                @Override
//                public void dbiCall(Emulator<?> emulator, Arm32RegisterContext ctx, HookEntryInfo info) { // 通过base+offset inline wrap内部函数，在IDA看到为sub_xxx那些
//                    System.out.println("R3=" + ctx.getLongArg(3) + ", R10=0x" + Long.toHexString(ctx.getR10Long()));
//                }
//            });
//
//            Dobby dobby = Dobby.getInstance(emulator);
//            dobby.replace(module.findSymbolByName("ss_encrypted_size"), new ReplaceCallback() { // 使用Dobby inline hook导出函数
//                @Override
//                public HookStatus onCall(Emulator<?> emulator, HookContext context, long originFunction) {
//                    System.out.println("ss_encrypted_size.onCall arg0=" + context.getIntArg(0) + ", originFunction=0x" + Long.toHexString(originFunction));
//                    return HookStatus.RET(emulator, originFunction);
//                }
//                @Override
//                public void postCall(Emulator<?> emulator, HookContext context) {
//                    System.out.println("ss_encrypted_size.postCall ret=" + context.getIntArg(0));
//                }
//            }, true);
//
//            IxHook xHook = XHookImpl.getInstance(emulator); // 加载xHook，支持Import hook，文档看https://github.com/iqiyi/xHook
//            xHook.register("libttEncrypt.so", "strlen", new ReplaceCallback() { // hook libttEncrypt.so的导入函数strlen
//                @Override
//                public HookStatus onCall(Emulator<?> emulator, HookContext context, long originFunction) {
//                    Pointer pointer = context.getPointerArg(0);
//                    String str = pointer.getString(0);
//                    System.out.println("strlen=" + str);
//                    context.push(str);
//                    return HookStatus.RET(emulator, originFunction);
//                }
//                @Override
//                public void postCall(Emulator<?> emulator, HookContext context) {
//                    System.out.println("strlen=" + context.pop() + ", ret=" + context.getIntArg(0));
//                }
//            }, true);
//            xHook.register("libttEncrypt.so", "memmove", new ReplaceCallback() {
//                @Override
//                public HookStatus onCall(Emulator<?> emulator, long originFunction) {
//                    RegisterContext context = emulator.getContext();
//                    Pointer dest = context.getPointerArg(0);
//                    Pointer src = context.getPointerArg(1);
//                    int length = context.getIntArg(2);
//                    Inspector.inspect(src.getByteArray(0, length), "memmove dest=" + dest);
//                    return HookStatus.RET(emulator, originFunction);
//                }
//            });
//            xHook.register("libttEncrypt.so", "memcpy", new ReplaceCallback() {
//                @Override
//                public HookStatus onCall(Emulator<?> emulator, long originFunction) {
//                    RegisterContext context = emulator.getContext();
//                    Pointer dest = context.getPointerArg(0);
//                    Pointer src = context.getPointerArg(1);
//                    int length = context.getIntArg(2);
//                    Inspector.inspect(src.getByteArray(0, length), "memcpy dest=" + dest);
//                    return HookStatus.RET(emulator, originFunction);
//                }
//            });
//            xHook.refresh(); // 使Import hook生效
//        }
//
//        if (logging) {
//            emulator.attach(DebuggerType.ANDROID_SERVER_V7); // 附加IDA android_server，可输入c命令取消附加继续运行
//        }
//        byte[] data = new byte[16];

//        调用 add 方法
//        int retval = NativeHelperUtils.callStaticJniMethodInt(emulator, "add(III)I", 0x100, 0x200, 0x300); // 执行Jni方法
//        System.out.println("res : 0x" + Integer.toHexString(retval));

//        调用MD5方法
//        StringObject md5_retval = NativeHelperUtils.callStaticJniMethodObject(emulator, "md5(Ljava/lang/String;)Ljava/lang/String;", new StringObject(vm,"123456")); // 执行Jni方法
//        System.out.println("md5_retval res : " + md5_retval.getValue());

//        调用encode 方法
//        StringObject encodeRetval = NativeHelperUtils.callStaticJniMethodObject(emulator, "encode()Ljava/lang/String;"); // 执行Jni方法
//        System.out.println("encodeRetval res : " + encodeRetval.getValue());


//        return md5_retval;

        // unicorn 原生hook
// hook_add_new 支持多种hook， new CodeHook 是指令级别的hook
//        emulator.getBackend().hook_add_new(new CodeHook() {
//            @Override
//            public void hook(Backend backend, long address, int size, Object user) {
//                System.out.println("unicorn ");
////                System.out.println(size);
////                System.out.println(user);
//                emulator.getUnwinder().unwind();
//                System.out.println("=================================");
//
//                // 获取到寄存器
//                Arm64RegisterContext ctx = emulator.getContext();
//                UnidbgPointer a = ctx.getPointerArg(0);
//                if (address == module.base+0x1FF4){
//                    UnidbgPointer md5Ctx = ctx.getPointerArg(0);
//                    Inspector.inspect(md5Ctx.getByteArray(0,32),"MD5Ctx");
//
//                    UnidbgPointer plainText = ctx.getPointerArg(1);
//                    Inspector.inspect(plainText.getByteArray(0,32),"plainText");
//                    int length = ctx.getIntArg(2);
//                    Inspector.inspect("length :",length);
//
//                }else if (address == module.base+0x2004){
//                    UnidbgPointer md5Ctx =ctx.getPointerArg(0);
//                    Inspector.inspect(md5Ctx.getByteArray(0,32),"MD5Ctx 222");
//                    UnidbgPointer cipherText = ctx.getPointerArg(1);
//                    Inspector.inspect(cipherText.getByteArray(0,32),"cipherText");
//
//                }
//            }
//
//            @Override
//            public void onAttach(UnHook unHook) {
//
//            }
//
//            @Override
//            public void detach() {
//
//            }
//        }, module.base + 0x22A0, module.base + 0x22A0, "hengdin");
//
//        StringObject md5_retval = NativeHelperUtils.callStaticJniMethodObject(emulator, "md5(Ljava/lang/String;)Ljava/lang/String;", new StringObject(vm,"123456")); // 执行Jni方法
//        System.out.println("md5_retval res : " + md5_retval.getValue());
        // 动态调试  --------------------------------------------------------------------------------------------
        // add 方法
//        Debugger debugger = emulator.attach();
//        debugger.addBreakPoint(module.base + 0x1AEC);
//        debugger.addBreakPoint(module.base + 0x1AF4);
//        int retval = NativeHelperUtils.callStaticJniMethodInt(emulator, "add(III)I", 0x100, 0x200, 0x300); // 执行Jni方法
//        System.out.println("res : 0x" + Integer.toHexString(retval));

//        debugger.addBreakPoint(module.base + 0x22A0);
//        debugger.addBreakPoint(module.base + 0x1F2C);
//
//        debugger.addBreakPoint(module.base + 0x3A78); // md5 final 位置
//
//
//        StringObject md5_retval = NativeHelperUtils.callStaticJniMethodObject(emulator, "md5(Ljava/lang/String;)Ljava/lang/String;", new StringObject(vm,"123456")); // 执行Jni方法
//        System.out.println("md5_retval res : " + md5_retval.getValue());

//        lesson 34
//        String traceFile = "log.log";
//        PrintStream traceStream = null;
//        try {
//            traceStream = new PrintStream(new FileOutputStream(traceFile),true);
//        } catch (FileNotFoundException e) {
//            throw new RuntimeException(e);
//        }
//        // traceRead  的参数1 为模块的起始地址，参数2 为监控的结束地址
//        // 正常使用的时候，可以把监控范围缩小。
//        emulator.traceRead(module.base, module.base+module.size).setRedirect(traceStream);
//        emulator.traceRead(module.base, module.base+module.size).setRedirect(traceStream);
//        // 调用函数
//        System.out.println(Long.toHexString(module.base));
//        StringObject md5_retval = NativeHelperUtils.callStaticJniMethodObject(emulator, "md5(Ljava/lang/String;)Ljava/lang/String;", new StringObject(vm,"123456")); // 执行Jni方法
//        System.out.println("md5_retval res : " + md5_retval.getValue());

//        lesson 35
        String fileStream = new String("trace.log");
        PrintStream printStream = null;
        try {
            printStream = new PrintStream(new FileOutputStream(fileStream));
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        }
        emulator.traceCode(module.base, module.base + module.size).setRedirect(printStream);
        int retval = NativeHelperUtils.callStaticJniMethodInt(emulator, "add(III)I", 0x100, 0x200, 0x300); // 执行Jni方法
        System.out.println("res : 0x" + Integer.toHexString(retval));
    }

}
